home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 8: LINUX Games / Linux Cubed Series 8 - LINUX Games.iso / games / amusemen / nolan-1.1 / nolan.tar / nolan.c < prev    next >
C/C++ Source or Header  |  1994-04-25  |  12KB  |  501 lines

  1. /* 
  2.  * nolan.c --- interactive Nolan chart test for character displays
  3.  *
  4.  * by Eric S. Raymond <esr@snark.thyrsus.com>
  5.  */
  6.  
  7. #include <stdio.h>
  8. #include <unistd.h>
  9. #include <string.h>
  10. #include <signal.h>
  11. #include <ctype.h>
  12. #include <assert.h>
  13.  
  14. #include "screen.h"
  15.  
  16. #define CONTINUE    "Press any key to continue..."
  17. #define FINISH        "Press any key to finish..."
  18.  
  19. typedef int    bool;
  20. #define TRUE    1
  21. #define FALSE    0
  22.  
  23. /***************************************************************************
  24.  *
  25.  * Quiz interpreter state variables
  26.  *
  27.  ***************************************************************************/
  28.  
  29. #define MAXLABEL    16    /* maximum screens per program */
  30. #define MAXNAME        12    /* maximum characters per screen label */
  31. #define MAXQUES        10    /* maximum questions per screen */
  32. #define MAXLINE        132    /* allow for 80 characters plus markup */
  33.  
  34. typedef struct
  35. {
  36.     int    y, x;            /* location of question prompt */
  37.     char    label[MAXNAME];    /* the question label */
  38. }
  39. ques_t;
  40.  
  41. static ques_t    questions[MAXQUES];    /* current questions */    
  42. static int    nquestions;        /* number of labels */
  43. static long    found;            /* user took this before */
  44. static int    people;            /* count of people in scorefile */
  45. static int     lineno = 0;        /* script line number */
  46. static int     row;            /* current screen line */
  47. static int    score;            /* current scale score */
  48.  
  49. typedef int    (*intfunc)();        /* function returning integer */
  50.  
  51. /*
  52.  * Attribute stack handling.  We have to maintain a stack of currently
  53.  * active attributes so we'll be able to handle nested highlight marks
  54.  * properly.
  55.  */
  56. #define ATTRIB_STACK    10    /* way more than we should ever need */
  57. static int attribs[ATTRIB_STACK] = {'w'};
  58. static int *ap = attribs;
  59.  
  60. static void highlight();
  61.  
  62. /* special next-screen values */
  63. #define EXIT    -1
  64. #define NEXT    -2
  65.  
  66. /***************************************************************************
  67.  *
  68.  * Variables and data specific to the Nolan chart interpretation
  69.  *
  70.  ***************************************************************************/
  71. #define USERFILE    "nolan.sco"
  72.  
  73. /*
  74.  * These give the proper position of the user's asterisk in the 45-degree
  75.  * tilted grid of dots that is the Nolan chart.
  76.  */
  77. #define min(x, y)    (((x) > (y)) ? (y) : (x))
  78. #define OVER(p, e)      min(10 - p, e)            /* dots over */
  79. #define DOWN(p, e)    (20 - ((p) + (e)))        /* lines down */
  80.  
  81. static int    personal, economic;    /* self-government scores */
  82. static int    down;            /* countdown to user's chart line */
  83. static int    over;            /* count of dots to skip */
  84. static int    scatter[11][11];    /* scattergram data */
  85.  
  86. /* political types --- values uable directly as highlights */
  87. #define AUT    'm'    /* Authoritarians */
  88. #define CEN    'w'    /* Centrists */
  89. #define CON    'b'    /* Conservatives */
  90. #define LEF    'r'    /* Left Liberals */
  91. #define LIB    'g'    /* Libertarians */
  92.  
  93. /*
  94.  * This array translates from grid position to political type.
  95.  * As usual, vertical index is personal self-government,
  96.  * horizontal is economic self-government.
  97.  */
  98. static char scatcolors[11][11] =
  99. {
  100.     AUT, AUT, AUT, AUT, AUT, CON, CON, CON, CON, CON, CON,
  101.     AUT, AUT, AUT, AUT, AUT, CON, CON, CON, CON, CON, CON,
  102.     AUT, AUT, AUT, AUT, AUT, CON, CON, CON, CON, CON, CON,
  103.     AUT, AUT, AUT, AUT, AUT, CEN, CON, CON, CON, CON, CON,
  104.     AUT, AUT, AUT, AUT, CEN, CEN, CEN, CON, CON, CON, CON,
  105.     LEF, LEF, LEF, CEN, CEN, CEN, CEN, CEN, LIB, LIB, LIB,
  106.     LEF, LEF, LEF, LEF, CEN, CEN, CEN, LIB, LIB, LIB, LIB,
  107.     LEF, LEF, LEF, LEF, LEF, CEN, LIB, LIB, LIB, LIB, LIB,
  108.     LEF, LEF, LEF, LEF, LEF, LIB, LIB, LIB, LIB, LIB, LIB,
  109.     LEF, LEF, LEF, LEF, LEF, LIB, LIB, LIB, LIB, LIB, LIB,
  110.     LEF, LEF, LEF, LEF, LEF, LIB, LIB, LIB, LIB, LIB, LIB,
  111. };
  112.  
  113. /***************************************************************************
  114.  *
  115.  * Initialization and wrapup
  116.  *
  117.  ***************************************************************************/
  118.  
  119. /*ARGSUSED0*/
  120. static void wrapquiz(int dummy)
  121. /* end the quiz, either normally or due to signal */
  122. {
  123.     endscreen();
  124.     exit(0);
  125.  
  126. static void initquiz(void)
  127. /* make sure we clean up properly on exit */
  128. {
  129.     (void) signal(SIGINT,wrapquiz);
  130.     (void) signal(SIGINT,wrapquiz);
  131.     (void) signal(SIGIOT,wrapquiz);        /* for assert(3) */
  132.     if (signal(SIGQUIT,SIG_IGN) != SIG_IGN)
  133.     (void) signal(SIGQUIT,wrapquiz);
  134.  
  135.     initscreen();
  136. }            
  137.  
  138. /***************************************************************************
  139.  *
  140.  * Utility functions
  141.  *
  142.  ***************************************************************************/
  143.  
  144. #define CENTER(str)    ((COLS - reallength(str)) / 2)
  145. #define RIGHT(str)    (COLS - reallength(str) - 1)
  146.  
  147. static int reallength(char *str)
  148. /* compute the "real" length of a string, not counting @{}-constructs */
  149. {
  150.     char    *cp;
  151.     int        i = 0;
  152.  
  153.     for (cp = str; *cp; cp++)
  154.     if (cp[0] == '@' && (cp[1] == '@' || cp[1] == '}'))
  155.     {
  156.         i++;
  157.         cp++;
  158.     }
  159.     else if (cp[0] == '}')
  160.         continue;
  161.     else if (cp[0] == '@')
  162.         i -= 2;
  163.     else
  164.         i++;
  165.  
  166.     return(i);
  167. }
  168.  
  169. /***************************************************************************
  170.  *
  171.  * The quiz interpreter
  172.  *
  173.  ***************************************************************************/
  174.  
  175. static void display(char *oline)
  176. /* display text, doing highlighting and parsing question blocks */
  177. {
  178.     char    *cp, *ep, line[MAXLINE];
  179.  
  180.     /*
  181.      * KLUGE ALERT:
  182.      *
  183.      * Avoid putting out a line feed on the last line, it may cause a scroll.
  184.      * Unfortunately, we have to copy the line to avoid modifying an immutable
  185.      * string.
  186.      */
  187.     (void) strcpy(line, oline);
  188.     if (row == LINES - 1 && (cp = strchr(line, '\n')))
  189.     *cp = '\0';
  190.  
  191.     over = 0;
  192.     for (cp = line; *cp; cp++)
  193.     {
  194.     if (cp[0] == '@' && (cp[1] == '@' || cp[1] == '}' || cp[1] == '%'))
  195.     {
  196.         ++cp;
  197.         sputc(*cp);
  198.     }
  199.     else if (cp[0] == '}' && ap > attribs)
  200.     {
  201.         if (ap > attribs)
  202.         --ap;
  203.         sattrset(*ap);
  204.     }
  205.     else if (cp[0] == '%')
  206.     {
  207.         /* these allow us to display the user's scores numerically */
  208.         if (cp[1] == 'E')
  209.         {
  210.         scprintf("%-3d", economic * 10);
  211.         cp +=2;
  212.         }
  213.         else if (cp[1] == 'P')
  214.         {
  215.         scprintf("%-3d", personal * 10);
  216.         cp += 2;
  217.         }
  218.         else if (cp[1] == 'N')
  219.         {
  220.         scprintf("%-3d", people);
  221.         cp += 2;
  222.         }
  223.         else
  224.         sputc(*cp);
  225.     }
  226.     else if (down == 0 && cp[0] == '.')
  227.     {
  228.         if (over == OVER(personal, economic))
  229.         {
  230.         highlight();
  231.         sputc('*');
  232.         sattrset(*ap);
  233.         }
  234.         else
  235.         sputc(*cp);
  236.         over++;
  237.     }
  238.     else if (cp[0] != '@')
  239.     {
  240.         sputc(*cp);
  241.     }
  242.     else
  243.     {
  244.         ep = strchr(cp, '}');
  245.         if (cp[2] != '{')
  246.         {
  247.         endscreen();
  248.         (void) fprintf(stderr,
  249.                    "quiz: bad markup syntax, line %d\n",
  250.                    lineno);
  251.         exit(1);
  252.         }
  253.  
  254.         if (ep == (char *)NULL)
  255.         {
  256.         endscreen();
  257.         (void) fprintf(stderr,
  258.                    "quiz: no markup terminator, line %d\n",
  259.                    lineno);
  260.         exit(1);
  261.         }
  262.  
  263.         /* character span from cp+3 to ep-1 is {}-enclosed part */
  264.         if (attrok(cp[1]))
  265.         {
  266.         sattrset(cp[1]);
  267.         *++ap = cp[1];
  268.         cp += 2;
  269.         continue;
  270.         }
  271.         else if (cp[1] == 'q')
  272.         {
  273.         /* accumulate this question label and its location */
  274.         screenloc(
  275.               &questions[nquestions].y,
  276.               &questions[nquestions].x);
  277.         *ep = '\0';
  278.         (void) strcpy(questions[nquestions].label, cp + 3);
  279.         *ep = '}';
  280.         nquestions++;
  281.  
  282.         /* skip this command in further parsing */
  283.         if (ep)
  284.             cp = ep;
  285.         }
  286.         else
  287.         {
  288.         endscreen();
  289.         (void) fprintf(stderr,
  290.                    "quiz: bad markup letter %c, line %d\n",
  291.                    cp[1], lineno);
  292.         exit(1);
  293.         }
  294.     }
  295.     }
  296.     row++;
  297.     down--;
  298. }
  299.  
  300. static void doquestions(void)
  301. /* interpret and ask the questions found by a screen parse */
  302. {
  303.     int    i, response;
  304.  
  305.     for (i = 0; i < nquestions; i++)
  306.     {
  307.     do {
  308.         movecursor(questions[i].y, questions[i].x);
  309.         response = tolower(sgetch());
  310.         highlight();
  311.         sputc(response);
  312.         sattrset(*ap);
  313.     } while
  314.         (response != 'y' && response != 'm' && response != 'n');
  315.  
  316.     if (response == 'y')
  317.         score += 2;
  318.     else if (response == 'm')
  319.         score += 1;
  320.     else if (response == 'n')
  321.         score += 0;
  322.     }
  323. }
  324.  
  325. static void scattergram(void)
  326. /* plot a scattergram from the response totals */
  327. {
  328.     int    p, e;
  329.  
  330.     for (p = 10; p >= 0; p--)
  331.     {
  332.     sattrset('b');
  333.     scprintf("%-3d", p * 10);
  334.     sattrset('w');
  335.     sputs(" |");
  336.     for (e = 0; e <= 10; e++)
  337.     {
  338.         sattrset(scatcolors[e][p]);
  339.         if (scatter[e][p])
  340.         scprintf(" %4d ", scatter[e][p]);
  341.         else
  342.         sputs("    . ");
  343.         sattrset('w');
  344.     }
  345.     sputs("\n");
  346.     }
  347.  
  348.     sputs("    +-----------------------------------------------------------------\n");
  349.     sattrset('r');
  350.     sputs("         0    10    20    30    40    50    60    70    80    90   100\n");
  351.     sattrset('w');
  352. }
  353.  
  354. static int screenend(int next, int *scoretype)
  355. /* handle end-of-screen */
  356. {
  357.     char    *exitprompt = (next == EXIT) ? FINISH : CONTINUE;
  358.  
  359.     doquestions();
  360.     if (scoretype)
  361.     *scoretype = score;
  362.  
  363.     /*
  364.      * The -1 offset below copes with curses implementations that
  365.      * don't do the right thing to suppress autoscroll when putting
  366.      * a character into the last screen position.
  367.      */
  368.     movecursor(LINES - 1, RIGHT(exitprompt) - 1);
  369.     highlight();
  370.     sputs(exitprompt);
  371.     sattrset(*ap);
  372.     (void) sgetch();
  373.  
  374.     return(next);
  375. }
  376.  
  377. /* ARGSUSED0 */
  378. static void screen(char *name)
  379. /* get ready for a new screen */
  380. {
  381. }
  382.  
  383. /***************************************************************************
  384.  *
  385.  * Quiz gets included here
  386.  *    The problem: we want the convenience of a data-driven program (being
  387.  * able to easily change the script contents and its markup with a text
  388.  * editor).  But...we *don't* want the script exposed to errors or malice.
  389.  * So --- we compile the script into executable code.  No muss, no fuss,
  390.  * and the retrieval is faster.
  391.  *    The only drawback is that it's now painful to modify the script and
  392.  * regenerate this program unless you have a UNIX to work with..
  393.  *
  394.  ***************************************************************************/
  395.  
  396. static int dispatch(void);
  397.  
  398. #include "script.h"
  399.  
  400. #ifndef STANDOUT
  401. #define STANDOUT    'y'
  402. #endif
  403.  
  404. static void highlight()
  405. /* start highlight --- define this here to pick up generated STANDOUT value */
  406. {
  407.     sattrset(STANDOUT[0]);
  408. }
  409.  
  410. static int dispatch(void)
  411. /* dispatch on user's political type */
  412. {
  413.     switch (scatcolors[personal][economic])
  414.     {
  415.     case AUT:
  416.     return(AUT_SCREEN);
  417.  
  418.     case CON:
  419.     return(CON_SCREEN);
  420.  
  421.     case CEN:
  422.     return(CEN_SCREEN);
  423.  
  424.     case LEF:
  425.     return(LEF_SCREEN);
  426.  
  427.     case LIB:
  428.     return(LIB_SCREEN);
  429.     }
  430.     return(NEXT);
  431. }
  432.  
  433. /***************************************************************************
  434.  *
  435.  * Main sequence
  436.  *
  437.  ***************************************************************************/
  438.  
  439. /*ARGSUSED0*/
  440. main(int argc, char *argv[])
  441. {
  442.     char    buf[BUFSIZ], *user = username();
  443.     FILE    *ufp = (FILE *)NULL;
  444.     long    offset;
  445.     int        screen;
  446.  
  447.     static bool quizline(char *line);
  448.  
  449.     /* see whether user has done this before */
  450.     found = -1;
  451.     offset = 0L;
  452.     if ((ufp = fopen(USERFILE, "r+")) != (FILE *)NULL)
  453.     {
  454.     int    pers, eco;
  455.  
  456.     while (fscanf(ufp, "%[^:]:%d:%d\n", buf, &pers, &eco) == 3)
  457.     {
  458.         if (strcmp(buf, user) == 0)
  459.         found = offset;
  460.         else
  461.         scatter[pers / 10][eco / 10]++;
  462.         offset = ftell(ufp);
  463.         people++;
  464.     }
  465.     }
  466.  
  467.     /* set up the screen */
  468.     initquiz();
  469.  
  470.     /* interpret the quiz */
  471.     screen = 0;
  472.     for (;;)
  473.     {
  474.     int    next = (*screenfuncs[screen])();
  475.  
  476.     if (next == EXIT)
  477.         break;
  478.     else if (next == NEXT)
  479.         screen++;
  480.     else
  481.         screen = next;
  482.     }
  483.  
  484.     /* we're done */
  485.     endscreen();
  486.  
  487.     /* record user's scores in case he/she takes the quiz again */
  488.     if (ufp)
  489.     {
  490.     if (found > -1)
  491.         (void) fseek(ufp, found, SEEK_SET);
  492.     (void) fprintf(ufp, "%s:%03d:%03d\n", user, personal*10, economic*10);
  493.     (void) fclose(ufp);
  494.     }
  495.  
  496.     return(0);
  497. }
  498.  
  499. /* nolan.c ends here */
  500.